﻿using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;
using System.Linq;
using System.Reflection;
using AppsGenerator.Utils;

namespace AppsGenerator
{
    /// <summary>
    /// Initialize for using the MEF
    /// </summary>
    public class BootStrapper
    {
        private CompositionContainer _container;

        /// <summary>
        /// The default constructor
        /// </summary>
        public void InitIoC()
        {
            this.CreateIoCInstances();
            this.IoCConfigure();
        }

        /// <summary>
        /// Configure for using the IoC Container
        /// </summary>
        private void IoCConfigure()
        {
            //An aggregate catalog that combines multiple catalogs
            var catalog = new AggregateCatalog();
            //Adds all the parts found in all assemblies in 
            //the same directory as the executing program
            catalog.Catalogs.Add(new AssemblyCatalog(Assembly.GetExecutingAssembly()));

            //Create the CompositionContainer with the parts in the catalog
            this._container = new CompositionContainer(catalog);

            try
            {
                //Fill the imports of this object
                this._container.ComposeParts(this);
            }
            catch (CompositionException compositionException)
            {
                throw new Exception(compositionException.ToString());
            }     
        } 

        /// <summary>
        /// Called by the bootstrapper's constructor at runtime to start the framework.
        /// </summary>
        private void CreateIoCInstances()
        {
            IoC.GetInstance = GetInstance;
            IoC.GetAllInstances = GetAllInstances;
        }

        /// <summary>
        /// Override this to provide an IoC specific implementation.
        /// </summary>
        /// <param name="serviceType">The service to locate.</param>
        /// <param name="key">The key to locate.</param>
        /// <returns>The located service.</returns>
        private object GetInstance(Type serviceType, string key)
        {
            var contract = string.IsNullOrEmpty(key) ? AttributedModelServices.GetContractName(serviceType) : key;
            var exports = _container.GetExportedValues<object>(contract);

            if (exports.Any())
                return exports.First();

            throw new Exception(string.Format("Could not locate any instances of contract {0}.", contract));
        }

        /// <summary>
        /// Override this to provide an IoC specific implementation
        /// </summary>
        /// <param name="serviceType">The service to locate.</param>
        /// <returns>The located services.</returns>
        private IEnumerable<object> GetAllInstances(Type serviceType)
        {
            return this._container.GetExportedValues<object>(AttributedModelServices.GetContractName(serviceType));
        }
    }
}
